//
//  Encypt.m
//  ios-loan
//
//  Created by ys on 15/12/2017.
//  Copyright © 2017 hxhg. All rights reserved.
//

#import "Encypt.h"
#import <Security/Security.h>
#import <CommonCrypto/CommonCrypto.h>
#import "MacroDefinition.h"

@implementation Encypt

+ (NSString *)getRandomAESKey {
    NSString *string = [[NSString alloc]init];
    for (int i = 0; i < 16; i++) {
        int number = arc4random() % 62;
        if (number < 10) {
            int figure = arc4random() % 10;
            NSString *tempString = [NSString stringWithFormat:@"%d", figure];
            string = [string stringByAppendingString:tempString];
        }
        else if (number < 36) {
            char character = (arc4random() % 26) + 65;
            NSString *tempString = [NSString stringWithFormat:@"%c", character];
            string = [string stringByAppendingString:tempString];
        }
        else {
            char character = (arc4random() % 26) + 97;
            NSString *tempString = [NSString stringWithFormat:@"%c", character];
            string = [string stringByAppendingString:tempString];
        }
    }
    if (!string.length) {
        NSLog(@"AES getRandomAESKey Error");
    }
    return string;
}

+ (NSData *)AESEncrypt:(NSData *)content WithKey:(NSString *)key {
  
    if (!key) {
        NSLog(@"AES AESEncryptionWithKey Error");
        return nil;
    }
    NSInteger aesKeySize = 16;
    char keyPtr[aesKeySize + 1];       //room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr));      //将字符串keyPtr的前sizeof(keyPtr)个字节置为0（包括'\0'）
    [key getCString:keyPtr
          maxLength:sizeof(keyPtr)
           encoding:NSUTF8StringEncoding];
  
    NSUInteger dataLength = [content length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;   //AES block size (currently, only 128-bit blocks are supported)
  
    void *buffer = malloc(bufferSize);
    bzero(buffer, sizeof(buffer));
  
    size_t numBytesEncrypted = 0;
  
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES,
                                          kCCOptionPKCS7Padding, /* Default is CBC Mode ,PKCS7 is OK*/
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          [[GetChannelApiByKey(@"ivString") dataUsingEncoding:NSUTF8StringEncoding] bytes],   /* initialization vector (optional) */
                                          [content bytes],
                                          dataLength,           /* input */
                                          buffer,
                                          bufferSize,           /* output */
                                          &numBytesEncrypted);

    if (cryptStatus == kCCSuccess) {
//        return [[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted] base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
  
    free(buffer); //free the buffer;
    NSLog(@"AES AESEncryptionWithKey Error");
    return nil;
}

+ (NSData *)AESDecrypt:(NSData *)content WithKey:(NSString *)key {
  
    if (!key) {
        NSLog(@"AES AESDecryptionWithKey Error");
        return nil;
    }
    NSInteger aesKeySize = 16;
    char keyPtr[aesKeySize + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
  
    NSUInteger dataLength = [content length];
  
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
  
    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptorStatus = CCCrypt(kCCDecrypt,
                                            kCCAlgorithmAES,
                                            kCCOptionPKCS7Padding,  /* Default is CBC Mode ,PKCS7 is OK*/
                                            keyPtr,
                                            kCCBlockSizeAES128,
                                            [[GetChannelApiByKey(@"ivString") dataUsingEncoding:NSUTF8StringEncoding] bytes],
                                            [content bytes],
                                            dataLength,
                                            buffer,
                                            bufferSize,
                                            &numBytesDecrypted);
    if (cryptorStatus == kCCSuccess) {
//        return [[NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted] base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    free(buffer);
    NSLog(@"AES AESDecryptionWithKey Error");
    return nil;
}

@end

@implementation NSString (RSA)

#pragma --- mark Encrypt with public key

- (NSString *)RSAEncryptWithPublicKey:(NSString *)pubKey {
    if(!self || !pubKey){
        return nil;
    }
    
    SecKeyRef keyRef = [self getPublicKey:pubKey];
    if(!keyRef){
        return nil;
    }
    return [[self encryptWithKeyRef:keyRef] base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
}


- (SecKeyRef)getPublicKey:(NSString *)pubKey {
    if (!pubKey) {
        return nil;
    }
    
    NSArray *speciStringArr = @[@" ",@"\n",@"\r",@"\t"];
    for (NSString *per in speciStringArr) {
        [pubKey stringByReplacingOccurrencesOfString:per withString:@""];
    }
    NSData *itemData = [[NSData alloc] initWithBase64EncodedString:pubKey options:NSDataBase64DecodingIgnoreUnknownCharacters];
    itemData = [self stripPublicKeyHeader:itemData];
    if(!itemData){
        return nil;
    }
    
    //a tag to read/write keychain storage
    NSString *tag = @"RSA_PubKey";
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
    // Delete any old lingering key with the same tag
    NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
    [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
    SecItemDelete((__bridge CFDictionaryRef)publicKey);
    
    // Add persistent version of the key to system keychain
    [publicKey setObject:itemData forKey:(__bridge id)kSecValueData];
    [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)
     kSecAttrKeyClass];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
     kSecReturnPersistentRef];
    
    CFTypeRef persistKey = nil;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
    if (persistKey != nil){
        CFRelease(persistKey);
    }
    if ((status != noErr) && (status != errSecDuplicateItem)) {
        return nil;
    }
    
    [publicKey removeObjectForKey:(__bridge id)kSecValueData];
    [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // Now fetch the SecKeyRef version of the key
    SecKeyRef keyRef = nil;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);
    if(status != noErr){
        return nil;
    }
    return keyRef;
}

- (NSData *)stripPublicKeyHeader:(NSData *)d_key {
    // Skip ASN.1 public key header
    if (d_key == nil) return(nil);
    
    unsigned long len = [d_key length];
    if (!len) return(nil);
    
    unsigned char *c_key = (unsigned char *)[d_key bytes];
    unsigned int  idx     = 0;
    
    if (c_key[idx++] != 0x30) return(nil);
    
    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;
    
    // PKCS #1 rsaEncryption szOID_RSA_RSA
    static unsigned char seqiod[] =
    { 0x30,   0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
        0x01, 0x05, 0x00 };
    if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
    
    idx += 15;
    
    if (c_key[idx++] != 0x03) return(nil);
    
    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;
    
    if (c_key[idx++] != '\0') return(nil);
    
    // Now make a new NSData from this buffer
    return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}


- (NSData *)encryptWithKeyRef:(SecKeyRef)keyRef {
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    const uint8_t *srcbuf = (const uint8_t *)[data bytes];
    size_t srclen = (size_t)data.length;
    
    size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);
    void *outbuf = malloc(block_size);
    size_t src_block_size = block_size - 11;
    
    /*  RSA_PADDING_TYPE_PKCS1 PKCS1填充模式 。
     输入：必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是　RSA_size(rsa) – 11
     如果输入的明文过长，必须切割，　然后填充
     输出：和modulus一样长
     */
    NSMutableData *ret = [NSMutableData data];
    for(int idx=0; idx<srclen; idx+=src_block_size){
        //        NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);
        size_t data_len = srclen - idx;
        if(data_len > src_block_size){
            data_len = src_block_size;
        }
        
        size_t outlen = block_size;
        OSStatus status = noErr;
        status = SecKeyEncrypt(keyRef,
                               kSecPaddingPKCS1,
                               srcbuf + idx,
                               data_len,
                               outbuf,
                               &outlen
                               );
        if (status != 0) {
            NSLog(@"%@: SecKeyEncrypt fail. Error Code: %d",NSStringFromClass(self.class),(int)status);
            ret = nil;
            break;
        }else{
            [ret appendBytes:outbuf length:outlen];
        }
    }
    
    free(outbuf);
    CFRelease(keyRef);
    return ret;
}

@end
